#include "primitive.hpp"
#include "limits"

Primitive::~Primitive()
{
}
Sphere::Sphere()
{
	m_sphere = new NonhierSphere(Point3D(0,0,0),1);
}
Sphere::~Sphere()
{
	delete m_sphere;
}

Cube::Cube()
{
	m_box = new NonhierBox(Point3D(0,0,0),1);
}

Cube::~Cube()
{
	delete m_box;
}

NonhierSphere::~NonhierSphere()
{
}
NonhierBox::~NonhierBox()
{
}


/*
 * RAY_LINE_FUNCTION: P[x,y,z] = P1[x1,y1,z1] + u(P2[x2,y2,z2] - P1[x1,y1,z1])
 * SPHERE_FUNCTION: (x-x3)^2 + (y-y3)^2 + (z-z3)^2 = r^2
 * INTERSECTION_FUNCTION: Au^2 + Bu + C = 0
 * -> A=(x2-x1)^2+(y2-y1)^2+(z2-z1)^2 = ray_direction(DOT)ray_direction
 * -> B=2[(x2-x1))(x1-x3)+(y2-y1)(y1-y3)+(z2-z1)(z1-z3)]
 *     =2(ray_diection)(DOT)(pointTOcenter)
 * -> C=x3^2+y3^2+z3^2+x1^2+y1^2+z1^2-2[x3x1+y3y1+z3z1] - radius^2
 *     = pointTOcenter(DOT)pointTOcenter - radius^2
 */
bool NonhierSphere::RayIntersect(Ray ray, Intersection *intersection)
{
	Vector3D pointTOcenter(ray.get_position() - m_pos);
	double A = (ray.get_direction()).dot(ray.get_direction());
	double B = 2*(ray.get_direction()).dot(pointTOcenter);
	double C = pointTOcenter.dot(pointTOcenter) - pow(m_radius,2);
	
	bool hit = false;	
	double roots[2];
	int num_root = quadraticRoots(A,B,C,roots);
	if(num_root == 0) return false;
	for(int i=0; i<num_root; i++){
		double u = roots[i];
		if(u > 1e-6 && u < intersection->m_dist){
			Point3D temp_point = ray.get_position() + u*(ray.get_direction());
			intersection->m_dist = u;
			intersection->m_intersect = temp_point;
			intersection->m_normal = temp_point - m_pos;
			intersection->m_normal.normalize();
			hit = true;
		}
	}
	return hit;
}

bool NonhierBox::RayIntersect(Ray ray, Intersection *intersection)
{
	double t_near = -std::numeric_limits<double>::infinity();
	double t_far = std::numeric_limits<double>::infinity();
	double t1, t2;
	Vector3D dir = ray.get_direction();
	Point3D pos = ray.get_position();
	Vector3D normal;
	for(int plane = 0; plane < 3; plane++){
		if(dir[plane] == 0.0 && (pos[plane] < near_vectex[plane] || pos[plane] > far_vectex[plane]))
			return false;
		else{
			t1 = ((double)near_vectex[plane] - (double)pos[plane])/(double)dir[plane];
			t2 = ((double)far_vectex[plane] - (double)pos[plane])/(double)dir[plane];

			if(t1>t2){
				double temp = t1;
				t1 = t2;
				t2 = temp;
			}
			if(t1>t_near){
				t_near = t1;
				normal = Vector3D(0.0,0.0,0.0);
				normal[plane] = -dir[plane];
			}
			if(t2<t_far) t_far = t2;
			if(t_near>t_far) return false;	// box is missed
			if(t_far<0) return false; 	// box is behind
		}
	}
	
	if(t_near > 1e-6 && t_near < intersection->m_dist){	
		intersection->m_dist = t_near;
		intersection->m_intersect = pos+t_near*dir;
		intersection->m_normal = normal;
		intersection->m_normal.normalize();
		return true;
	}
	return false;
}

bool Sphere::RayIntersect(Ray ray, Intersection *intersection)
{
	return m_sphere->RayIntersect(ray, intersection);
}

bool Cube::RayIntersect(Ray ray, Intersection *intersection)
{
	return m_box->RayIntersect(ray,intersection);
}
